///
// * jdbm LICENSE v1.00
// *
// * Redistribution and use of this software and associated documentation
// * ("Software"), with or without modification, are permitted provided
// * that the following conditions are met:
// *
// * 1. Redistributions of source code must retain copyright
// *    statements and notices.  Redistributions must also contain a
// *    copy of this document.
// *
// * 2. Redistributions in binary form must reproduce the
// *    above copyright notice, this list of conditions and the
// *    following disclaimer in the documentation and/or other
// *    materials provided with the distribution.
// *
// * 3. The name "jdbm" must not be used to endorse or promote
// *    products derived from this Software without prior written
// *    permission of Cees de Groot.  For written permission,
// *    please contact cg@cdegroot.com.
// *
// * 4. Products derived from this Software may not be called "jdbm"
// *    nor may "jdbm" appear in their names without prior written
// *    permission of Cees de Groot. 
// *
// * 5. Due credit should be given to the jdbm Project
// *    (http://jdbm.sourceforge.net/).
// *
// * THIS SOFTWARE IS PROVIDED BY THE ndbm PROJECT AND CONTRIBUTORS
// * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
// * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
// * CEES DE GROOT OR ANY CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// * OF THE POSSIBILITY OF SUCH DAMAGE.
// *
// * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
// * Contributions are Copyright (C) 2000 by their associated contributors.
// *
// * $Id: PageManager.java,v 1.3 2005/06/25 23:12:32 doomdark Exp $
// 


//*************************************************************************
//**  Included in JDMB 1.0 port to SharpDBM;  11/2013 Cyrus Neah cneah@codingreal.com
//**  SharpDBM is an independent reimplementation of the JDBM 1.0 software library in C#  
//*************************************************************************


using SharpDBM.helper;


namespace SharpDBM.recman
{


///
// *  This class manages the linked lists of pages that make up a file.
// 
	internal   class PageManager
	{
	// our record file
		private RecordFile file;
	// header data
		private FileHeader header;
		private BlockIo headerBuf;

//    *
//     *  Creates a new page manager using the indicated record file.
//     
 
//
		internal PageManager(RecordFile file)
		{
			this.file = file;

		// check the file header. If the magic is 0, we assume a new
		// file. Note that we hold on to the file header node.
			headerBuf = file.get(0);
			if (headerBuf.readShort(0) == 0)
				header = new FileHeader(headerBuf, true);
			else
				header = new FileHeader(headerBuf, false);
		}

//    *
//     *  Allocates a page of the indicated type. Returns recid of the
//     *  page.
//     
 
//
		internal virtual long allocate(short type)
		{

			if (type == Magic.FREE_PAGE)
				throw new Error("allocate of free page?");

		// do we have something on the free list?
			long retval = header.getFirstOf(Magic.FREE_PAGE);
			bool isNew = false;
			if (retval != 0)
			{
			// yes. Point to it and make the next of that page the
			// new first free page.
				header.setFirstOf(Magic.FREE_PAGE, getNext(retval));
			}
			else
			{
			// nope. make a new record
				retval = header.getLastOf(Magic.FREE_PAGE);
				if (retval == 0)
				// very new file - allocate record #1
					retval = 1;
				header.setLastOf(Magic.FREE_PAGE, retval + 1);
				isNew = true;
			}

		// Cool. We have a record, add it to the correct list
			BlockIo buf = file.get(retval);
			PageHeader pageHdr = isNew ? new PageHeader(buf, type) : PageHeader.getView(buf);
			long oldLast = header.getLastOf(type);

		// Clean data.
			System.Array.Copy(RecordFile.cleanData, 0, buf.getData(), 0, RecordFile.BLOCK_SIZE);
			pageHdr.setType(type);
			pageHdr.setPrev(oldLast);
			pageHdr.setNext(0);


			if (oldLast == 0)
			// This was the first one of this type
				header.setFirstOf(type, retval);
			header.setLastOf(type, retval);
			file.release(retval, true);

		// If there's a previous, fix up its pointer
			if (oldLast != 0)
			{
				buf = file.get(oldLast);
				pageHdr = PageHeader.getView(buf);
				pageHdr.setNext(retval);
				file.release(oldLast, true);
			}

		// remove the view, we have modified the type.
			buf.setView(null);

			return retval;
		}

//    *
//     *  Frees a page of the indicated type.
//     
 
//
		internal virtual void free(short type, long recid)
		{
			if (type == Magic.FREE_PAGE)
				throw new Error("free free page?");
			if (recid == 0)
				throw new Error("free header page?");

		// get the page and read next and previous pointers
			BlockIo buf = file.get(recid);
			PageHeader pageHdr = PageHeader.getView(buf);
			long prev = pageHdr.getPrev();
			long next = pageHdr.getNext();

		// put the page at the front of the free list.
			pageHdr.setType(Magic.FREE_PAGE);
			pageHdr.setNext(header.getFirstOf(Magic.FREE_PAGE));
			pageHdr.setPrev(0);

			header.setFirstOf(Magic.FREE_PAGE, recid);
			file.release(recid, true);

		// remove the page from its old list
			if (prev != 0)
			{
				buf = file.get(prev);
				pageHdr = PageHeader.getView(buf);
				pageHdr.setNext(next);
				file.release(prev, true);
			}
			else
			{
				header.setFirstOf(type, next);
			}
			if (next != 0)
			{
				buf = file.get(next);
				pageHdr = PageHeader.getView(buf);
				pageHdr.setPrev(prev);
				file.release(next, true);
			}
			else
			{
				header.setLastOf(type, prev);
			}

		}


//    *
//     *  Returns the page following the indicated block
//     
 
//
		internal virtual long getNext(long block)
		{
			try
			{
				return PageHeader.getView(file.get(block)).getNext();
			}
			finally
			{
				file.release(block, false);
			}
		}

//    *
//     *  Returns the page before the indicated block
//     
 
//
		internal virtual long getPrev(long block)
		{
			try
			{
				return PageHeader.getView(file.get(block)).getPrev();
			}
			finally
			{
				file.release(block, false);
			}
		}

//    *
//     *  Returns the first page on the indicated list.
//     
 
//
		internal virtual long getFirst(short type)
		{
			return header.getFirstOf(type);
		}

//    *
//     *  Returns the last page on the indicated list.
//     
 
//
		internal virtual long getLast(short type)
		{
			return header.getLastOf(type);
		}


//    *
//     *  Commit all pending (in-memory) data by flushing the page manager.
//     *  This forces a flush of all outstanding blocks (this it's an implicit
//     *  {@link RecordFile#commit} as well).
//     
 
//
		internal virtual void commit()
		{
		// write the header out
			file.release(headerBuf);
			file.commit();

		// and obtain it again
			headerBuf = file.get(0);
			header = new FileHeader(headerBuf, false);
		}

//    *
//     *  Flushes the page manager. This forces a flush of all outstanding
//     *  blocks (this it's an implicit {@link RecordFile#commit} as well).
//     
 
//
		internal virtual void rollback()
		{
		// release header
			file.discard(headerBuf);
			file.rollback();
		// and obtain it again
			headerBuf = file.get(0);
			if (headerBuf.readShort(0) == 0)
				header = new FileHeader(headerBuf, true);
			else
				header = new FileHeader(headerBuf, false);
		}

//    *
//     *  Closes the page manager. This flushes the page manager and releases
//     *  the lock on the header.
//     
 
//
		internal virtual void close()
		{
			file.release(headerBuf);
			file.commit();
			headerBuf = null;
			header = null;
			file = null;
		}

//    *
//     *  Returns the file header.
//     
		internal virtual FileHeader getFileHeader()
		{
			return header;
		}

	}

}